home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / mail / delivery / smail-3.003 / smail-3
Text File  |  1994-11-22  |  46KB  |  1,482 lines

  1.           SMTP Extensions for Smail 3.1.29.1
  2.                   Version 1
  3.         by Simon Leinen <simon@lia.di.epfl.ch>
  4.  
  5.                  DESCRIPTION
  6.                  -----------
  7. This file contains a patch to the Smail 3.1.29.1 sources that
  8. implements RFCs 1425, 1426 and 1427 as well as the Internet Draft
  9. `draft-ietf-smtpext-pipeline-02.txt'.  The following features are
  10. implemented:
  11.  
  12. * The new EHLO command is recognized as mandated by [RFC1425].  The
  13.   list of supported extensions in the reply includes HELP, EXPN and
  14.   optionally 8BITMIME and SIZE.  If a message is received from a
  15.   remote mailer that said EHLO, the Received: line that is added to
  16.   the message will contain a sender protocol of "esmtp" rather than
  17.   "stmp" (or "ebsmtp" rather than "bsmtp").
  18.  
  19. * The PIPELINING extension is provided by the server (this did not
  20.   necessiatate any modifications of how the server works) and used by
  21.   the client.  The PIPELINING extension reduces the number of
  22.   turn-arounds in SMTP conversations between two mailers that both
  23.   support it.  This can be a big win over slow IP networks.  The
  24.   implementation is based on the Internet Draft
  25.   `draft-ietf-smtpext-pipeline-02.txt' which is available from many
  26.   archive sites that have IETF documents.
  27.  
  28. * BODY [RFC1426] and SIZE [RFC1427] clauses are recognized in MAIL
  29.   FROM: SMTP commands on the receiver side.  Currently it doesn't
  30.   matter whether the BODY type is 7BIT or 8BITMIME - Smail is always
  31.   8-bit transparent.  If the value passed in a SIZE clause exceeds
  32.   max_message_size, a "552 message too large" error reply is
  33.   generated.
  34.  
  35. * The sender side tries to greet the receiving SMTP with an EHLO
  36.   command.  If this fails, we send the standard HELO greeting.  If
  37.   this also fails, we try RSET to clear up the remote mailer and then
  38.   HELO.  If the EHLO is accepted, Smail parses the response to find
  39.   out whether the receiver understands SIZE and BODY.  A maximum
  40.   message size that is provided by the receiving SMTP is currently
  41.   ignored, but the MAIL FROM: command is decorated with a SIZE
  42.   declaration based on a guess of the spooled message's size.  The
  43.   BODY clause is never issued.
  44.  
  45. * NEW (November 14, 1994): If HAVE_DF_SPOOL is defined, Smail will try
  46.   to dynamically find out how much space is available for spooling
  47.   incoming files.  The announced SIZE will be limited by available
  48.   disk space in addition to the administrative limit imposed by
  49.   max_message_size.  The code that computes free disk space needs
  50.   either the statvfs() or the statfs() call.  HAVE_STATVFS should be
  51.   defined on systems that have statvfs().  If your system doesn't have
  52.   statvfs(), HAVE_SYS_STATFS_H should be defined if you have the
  53.   <sys/statfs.h> header.
  54.  
  55. * NEW: The SMTP greeting line (smtp_banner) now has a different
  56.   default value when HAVE_EHLO is defined.  The new greeting is two
  57.   lines long, and the second line starts with the string "ESMTP".  It
  58.   seems that this is necessary to activate ESMTP in a sending SMTP
  59.   agent that is based on Sendmail 8.  The two-line greeting (although
  60.   perfectly legal according to the RFCs) *might* cause problems with
  61.   legacy mailers.  Fortunately I haven't found such a case yet.  In
  62.   case you have problems with other mailers not being able to connect
  63.   to yours, you can always override the `smtp_banner' variable in one
  64.   of Smail's configuration files.  I would also be interested in
  65.   hearing about such broken mailers if they exist.
  66.  
  67.                  INSTALLATION
  68.                  ------------
  69. Apply the enclosed patch to the top-level directory of the Smail
  70. distribution (the one that contains the src/ subdirectory).  You will
  71. need a fairly recent version of the "patch" program because the patch
  72. is in the newer, more compact and more readable Unidiff format.
  73.  
  74. The server side of the extensions cannot be disabled in this version.
  75. This means that EHLO is always accepted as an SMTP greeting, and the
  76. SIZE parameter is accepted.
  77.  
  78. In order to enable the extensions, the following symbols can be added
  79. to the HAVE variable in the EDITME file:
  80.  
  81.     EHLO    enables client-side support for EHLO, SIZE and
  82.         PIPELINING
  83.     ESMTP_8BITMIME
  84.         to enable minimal server-side RFC 1426 support.
  85.         Basically the BODY clause is accepted and ignored.
  86.     DF_SPOOL
  87.         causes free disk space to be checked dynamically when
  88.         determining the maximum size of messages that will be
  89.         accepted.  This needs other symbols such as
  90.         HAVE_STATVFS or HAVE_SYS_STAT_H to be defined in turn.
  91.  
  92.                    CAVEATS
  93.                    -------
  94. Here is a possible problem with ESMTP support: Apparently there are
  95. some non-RFC821-conformant gateways running on legacy systems that
  96. react to the EHLO query by closing down the connection rather than
  97. giving a "500 command unrecognized" error.  Smail handles this
  98. situation like a normal link failure, so a message sent via SMTP to
  99. such a mailer will presumably get bounced after the retry period is
  100. over.  If you care about interoperatibility with such weird systems,
  101. you probably don't want to use the SMTP extensions, or write some code
  102. for Smail to handle this situation more cleverly (see the TODO section
  103. below).  Personally I think that this is such an extreme case of
  104. protocol incompatibility that I haven't taken the time to support
  105. these mailer daemons.
  106.  
  107. When an ESMTP-capable client Smail talks to a non-ESMTP-capable SMTP
  108. server, there are two additional query/reply pairs for each SMTP
  109. connection.  This is because Smail issues EHLO to find out whether the
  110. recipient mailer handles ESMTP, gets an error message and then sends
  111. an RSET before retrying with HELO.  If you have extremely slow or
  112. unreliable SMTP connections, or if your network connections are
  113. charged by the packet this may matter to you.
  114.  
  115.                  TODO
  116.                  ----
  117. Right now the SMTP extensions don't really give any new functionality,
  118. except that max_message_size is *somewhat* enforced, but only for
  119. cooperating (RFC 1427-savvy) client mailers.  The following
  120. improvements to the code might be useful:
  121.  
  122. * Smail should be extended to handle 8-bit text messages in a more
  123.   standard way.  Currently it is simply 8-bit transparent, which is
  124.   seen as a feature by most users.  However, sending non-7-bit-ASCII
  125.   characters to remote mailer daemons is a violation of RFC 821 and
  126.   can lead to interoperability problems.  Even when communicating over
  127.   8-bit clean SMTP networks, the "Just-Send-8" approach has the
  128.   problem that it is not clear which character set has been used by
  129.   the author [RFC1428].
  130.  
  131.   In principle, all these problems are solved by the MIME standard
  132.   [RFC1341, RFC1342], which is rapidly gaining acceptance.  In a world
  133.   where everybody uses MIME-capable mailers, there wouldn't be any
  134.   eighth bits to be transported.  But as long as people still send
  135.   8-bit messages, Smail could convert such messages to 7bit-MIME when
  136.   they are transported over an SMTP network.  RFC1428 describes how
  137.   this could be done.
  138.  
  139. * If the "link broken by EHLO" problem really does occur in the real
  140.   world, it has to be handled by Smail's SMTP client side.  For
  141.   instance, once an EHLO command causes a link to be broken, the
  142.   receiving host should be noted in a "black list" by a mechanism
  143.   similar to the retry file mechanism.  Hosts should be removed from
  144.   the black list periodically, so that when they are upgraded to
  145.   SMTP-conforming software, the new functionality will be used.  It
  146.   would also be nice if the "retry" file could be extended by
  147.   something like "ehlo/noehlo" keyword that would allow specific
  148.   hosts/domains to be excluded from ESMTP trials.
  149.  
  150.                   ReFerenCes
  151.  
  152. 1344  N. Borenstein, "Implications of MIME for Internet Mail Gateways",
  153.       06/11/1992. (Pages=9) (Format=.txt, .ps)
  154.  
  155. 1425  J. Klensin, N. Freed, M. Rose, E. Stefferud, D. Crocker, "SMTP
  156.       Service Extensions", 02/10/1993. (Pages=10) (Format=.txt)
  157.  
  158. 1426  J. Klensin, N. Freed, M. Rose, E. Stefferud, D. Crocker, "SMTP
  159.       Service Extension for 8bit-MIMEtransport", 02/10/1993. (Pages=6)
  160.       (Format=.txt)
  161.       
  162. 1427  K. Moore, N. Freed, J. Klensin, "SMTP Service Extension for
  163.       Message Size Declaration", 02/10/1993. (Pages=8) (Format=.txt)
  164.       
  165. 1428  G. Vaudreuil, "Transition of Internet Mail from Just-Send-8 to
  166.       8Bit-SMTP/MIME", 02/10/1993. (Pages=6) (Format=.txt)
  167.       
  168. 1521  N. Borenstein, N. Freed, "MIME (Multipurpose Internet Mail
  169.       Extensions) Part One: Mechanisms for Specifying and Describing
  170.       the Format of Internet Message Bodies", 09/23/1993. (Pages=81)
  171.       (Format=.txt) (Obsoletes RFC1341)
  172.  
  173. 1522  K. Moore, "MIME (Multipurpose Internet Mail Extensions) Part Two:
  174.       Message Header Extensions for Non-ASCII Text",
  175.       09/23/1993. (Pages=10) (Format=.txt) (Obsoletes RFC1342)
  176.  
  177. --- src/Makefile    1994/11/14 09:19:33    1.1
  178. +++ src/Makefile    1994/11/16 17:35:22
  179. @@ -1,5 +1,5 @@
  180.  #!/bin/make -f
  181. -# @(#) $Id: Makefile,v 1.1 1994/11/14 09:19:33 logiciel Exp logiciel $
  182. +# @(#) $Id: Makefile,v 1.22 1992/09/20 17:13:22 tron Exp $
  183.  # Makefile for the smail program
  184.  #
  185.  #    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
  186. @@ -167,6 +167,10 @@
  187.          (cd $$i; ${MK} SRC_PREFIX=${SRC_PREFIX}$$i/ depend); \
  188.       done
  189.  
  190. +iobpeek.h: ${DEFS_SH}
  191. +    @rm -f iobpeek.h
  192. +    @. ./${DEFS_SH}; ${XEXEC} ${SHELL} geniobpeek.sh $$CC "$$CFLAGS"
  193. +
  194.  depend: ${SRC} check_defs subdir_depend local_depend
  195.  
  196.  subdir_depend: remove_driver_makefiles \
  197. @@ -303,5 +307,6 @@
  198.  
  199.  # special dependency:
  200.  version.o: version.h
  201. +smtprecv.o: iobpeek.h
  202.  
  203.  # DO NOT REMOVE THIS LINE, OR "make depend" WILL NOT WORK
  204. --- src/addr.c    1994/11/14 09:20:09    1.1
  205. +++ src/addr.c    1994/11/14 09:23:13
  206. @@ -63,6 +63,16 @@
  207.      char *address;            /* address to be preparsed */
  208.      char **error;            /* return error message here */
  209.  {
  210. +    char * ignore;
  211. +    return preparse_address_1(address, error, & ignore);
  212. +}
  213. +
  214. +char *
  215. +preparse_address_1(address, error, rest)
  216. +    char *address;
  217. +    char **error;
  218. +    char **rest;
  219. +{
  220.      register char *ap;            /* temp for scanning address */
  221.      char *mark_start = NULL;        /* marked position of < */
  222.      char *mark_end = NULL;        /* marked position of > */
  223. @@ -125,9 +135,10 @@
  224.      ap = xmalloc((unsigned)(strlen(address) + 1));
  225.      (void) strcpy(ap, address);
  226.      if (mark_end) {
  227. -        *mark_end = '>';        /* widden the original address */
  228. +        *mark_end++ = '>';        /* widden the original address */
  229.      }
  230.      DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", ap);
  231. +    *rest = mark_end;
  232.      return ap;            /*  no transformations */
  233.      }
  234.  
  235. @@ -144,9 +155,10 @@
  236.          ap = xmalloc((unsigned)(strlen(address) + 1));
  237.          (void) strcpy(ap, address);
  238.          if (mark_end) {
  239. -        *mark_end = '>';    /* widden the original address */
  240. +        *mark_end++ = '>';    /* widden the original address */
  241.          }
  242.          DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
  243. +        *rest = mark_end;
  244.          return ap;        /* address should be okay */
  245.      }
  246.      ap++;
  247. @@ -157,7 +169,7 @@
  248.          /* first part already !-route */
  249.          (void) strncpy(p, address, ap-address);
  250.          if (mark_end) {
  251. -        *mark_end = '>';    /* widden the original address */
  252. +        *mark_end++ = '>';    /* widden the original address */
  253.          }
  254.          ap = build_uucp_route(ap, error); /* build !-route */
  255.          if (ap == NULL) {
  256. @@ -167,15 +179,17 @@
  257.          (void) strcat(p, ap);    /* concatenate together */
  258.          xfree(ap);
  259.          DEBUG1(DBG_ADDR_HI, "preparse_address returns: %s\n", p);
  260. +        *rest = mark_end;
  261.          return p;            /* transformed */
  262.      }
  263.      }
  264.      ap = xmalloc((unsigned)(strlen(address) + 1));
  265.      (void) strcpy(ap, address);
  266.      if (mark_end) {
  267. -    *mark_end = '>';    /* widden the original address */
  268. +    *mark_end++ = '>';    /* widden the original address */
  269.      }
  270.      DEBUG1(DBG_ADDR_HI, "preparse address returns: %s\n", ap);
  271. +    *rest = mark_end;
  272.      return ap;                /* no transformations */
  273.  }
  274.  
  275. --- src/config.h    1994/11/14 09:53:56    1.1
  276. +++ src/config.h    1994/11/14 21:32:07
  277. @@ -663,7 +663,11 @@
  278.   * SMTP startup banner message
  279.   */
  280.  #ifndef SMTP_BANNER
  281. -# define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date"
  282. +# ifdef HAVE_EHLO
  283. +#  define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date\nESMTP supported"
  284. +# else
  285. +#  define SMTP_BANNER "$primary_name Smail$version #$compile_num ready at $date"
  286. +# endif
  287.  #endif
  288.  
  289.  /*
  290. @@ -939,7 +943,11 @@
  291.  # ifdef    SMALL_MEMORY
  292.  #  define MESSAGE_BUF_SIZE    BUFSIZ
  293.  # else
  294. -#  define MESSAGE_BUF_SIZE    MAX_MESSAGE_SIZE
  295. +#  if MAX_MESSAGE_SIZE == 0
  296. +#   define MESSAGE_BUF_SIZE    (256*1024)
  297. +#  else
  298. +#   define MESSAGE_BUF_SIZE    MAX_MESSAGE_SIZE
  299. +#  endif
  300.  # endif
  301.  #endif
  302.  
  303. --- src/extern.h    1994/11/14 09:23:47    1.1
  304. +++ src/extern.h    1994/11/14 17:52:22
  305. @@ -237,6 +237,7 @@
  306.   */
  307.  /* external functions defined in addr.c */
  308.  extern char *preparse_address();
  309. +extern char *preparse_address_1();
  310.  extern int parse_address();
  311.  extern char *address_token();
  312.  extern char *back_address_token();
  313. @@ -460,6 +461,7 @@
  314.  extern int new_grade();
  315.  extern void defer_message();
  316.  extern long message_date();
  317. +extern long spool_max_free_space();
  318.  
  319.  /* external functions defined in string.c */
  320.  extern int strcmpic();
  321. --- src/smtprecv.c    1994/11/14 09:24:09    1.1
  322. +++ src/smtprecv.c    1994/11/14 19:59:11
  323. @@ -22,6 +22,7 @@
  324.  #include "dys.h"
  325.  #include "log.h"
  326.  #include "hash.h"
  327. +#include "iobpeek.h"
  328.  #ifndef DEPEND
  329.  # include "extern.h"
  330.  # include "debug.h"
  331. @@ -45,6 +46,7 @@
  332.  /* types local to this file */
  333.  enum e_smtp_commands {
  334.      HELO_CMD,                /* HELO domain */
  335. +    EHLO_CMD,                /* EHLO domain */
  336.      MAIL_CMD,                /* MAIL FROM:<sender> */
  337.      RCPT_CMD,                /* RCPT TO:<recipient> */
  338.      DATA_CMD,                /* DATA */
  339. @@ -60,15 +62,33 @@
  340.  };
  341.  
  342.  /* functions local to this file */
  343. +#ifdef __STDC__
  344. +static void reset_state(void);
  345. +static enum e_smtp_commands read_smtp_command(FILE *, FILE *);
  346. +static void expand_addr(char *, FILE *);
  347. +static int verify_addr(char *, FILE *, int);
  348. +static void smtp_input_signals(void);
  349. +static void smtp_processing_signals(void);
  350. +static void set_term_signal(int);
  351. +static void smtp_receive_timeout_sig(int);
  352. +static void smtp_sig_unlink(int);
  353. +#ifdef HAVE_DF_SPOOL
  354. +static long compute_max_message_size_from_df_spool (void);
  355. +#endif
  356. +#else /* not __STDC__ */
  357.  static void reset_state();
  358.  static enum e_smtp_commands read_smtp_command();
  359.  static void expand_addr();
  360. -static void verify_addr();
  361. +static int verify_addr();
  362.  static void smtp_input_signals();
  363.  static void smtp_processing_signals();
  364.  static void set_term_signal();
  365.  static void smtp_receive_timeout_sig();
  366.  static void smtp_sig_unlink();
  367. +#ifdef HAVE_DF_SPOOL
  368. +static long compute_max_message_size_from_df_spool ();
  369. +#endif
  370. +#endif /* not __STDC__ */
  371.  
  372.  /* variables local to this file */
  373.  static char *data;            /* interesting data within input */
  374. @@ -80,6 +100,7 @@
  375.      "250-The following SMTP commands are recognized:",
  376.      "250-",
  377.      "250-   HELO hostname                   - startup and give your hostname",
  378. +    "250-   EHLO hostname                   - startup with extension info",
  379.      "250-   MAIL FROM:<sender-address>      - start transaction from sender",
  380.      "250-   RCPT TO:<recipient-address>     - name recipient for message",
  381.      "250-   VRFY <address>                  - verify deliverability of address",
  382. @@ -98,6 +119,15 @@
  383.      "250 Multiple messages may be specified.  End the last one with a QUIT."
  384.  };
  385.  
  386. +typedef enum {
  387. +    BF_unspecified,
  388. +    BF_7bit,
  389. +    BF_8bitmime
  390. +}
  391. +BodyFormat;
  392. +
  393. +#define BS_UNSPECIFIED -1L
  394. +typedef long BodySize;
  395.  
  396.  
  397.  /*
  398. @@ -132,6 +162,9 @@
  399.      FILE *save_errfile = errfile;
  400.      int save_debug = debug;
  401.      int temp, i, c;
  402. +    char *rest;
  403. +    int ehlo_p = 0;
  404. +    long accepted_msg_size = max_message_size;
  405.  
  406.      /* initialize state */
  407.      initialize_state();
  408. @@ -205,11 +238,14 @@
  409.      if (out) {
  410.          alarm(smtp_receive_command_timeout);
  411.      }
  412. -    switch (read_smtp_command(in)) {
  413. +    switch (read_smtp_command(in, out)) {
  414. +    case EHLO_CMD:
  415. +        ehlo_p = 1;
  416.      case HELO_CMD:
  417.          strip_rfc822_comments(data);
  418.          if (out && data[0] == '\0') {
  419. -        fprintf(out, "501 HELO requires domain name as operand\r\n");
  420. +        fprintf(out, "501 %s requires domain name as operand\r\n",
  421. +            ehlo_p ? "EHLO" : "HELO");
  422.          fflush(out);
  423.          break;
  424.          }
  425. @@ -217,38 +253,72 @@
  426.          sender_host = COPY_STRING(data);
  427.          }
  428.          if (sender_proto == NULL) {
  429. -        sender_proto = (out? "smtp": "bsmtp");
  430. +        sender_proto = (out? ehlo_p? "esmtp": "smtp"
  431. +                : ehlo_p? "ebsmtp": "bsmtp");
  432.          }
  433. +        if (! ehlo_p) {
  434.          if (out) {
  435.          fprintf(out, "250 %s Hello %s\r\n", primary_name, data);
  436.          fflush(out);
  437.          }
  438.          reset_state();
  439.          break;
  440. +        }
  441. +        strip_rfc822_comments(data);
  442. +        if (out && data[0] == '\0') {
  443. +        fprintf(out, "501 EHLO requires domain name as operand\r\n");
  444. +        fflush(out);
  445. +        break;
  446. +        }
  447. +        if (sender_host == NULL && data[0] != '\0') {
  448. +        sender_host = COPY_STRING(data);
  449. +        }
  450. +        if (sender_proto == NULL) {
  451. +        sender_proto = (out? "smtp": "bsmtp");
  452. +        }
  453. +        if (out) {
  454. +        fprintf(out, "250-%s Hello %s, here's what we support:\r\n",
  455. +            primary_name, data);
  456. +        fprintf(out, "250-EXPN\r\n");
  457. +#ifdef HAVE_DF_SPOOL
  458. +        accepted_msg_size = compute_max_message_size_from_df_spool ();
  459. +        if (accepted_msg_size == -1 
  460. +            || (max_message_size && max_message_size < accepted_msg_size))
  461. +            accepted_msg_size = max_message_size;
  462. +#endif
  463. +        if (accepted_msg_size && accepted_msg_size != -1) {
  464. +            fprintf(out, "250-SIZE %d\r\n", accepted_msg_size);
  465. +        } else {
  466. +            fprintf(out, "250-SIZE\r\n");
  467. +        }
  468. +#ifdef HAVE_ESMTP_8BITMIME
  469. +        fprintf(out, "250-8BITMIME\r\n");
  470. +#endif
  471. +        fprintf(out, "250-PIPELINING\r\n");
  472. +        fprintf(out, "250 HELP\r\n");
  473. +        fflush(out);
  474. +        }
  475. +        reset_state();
  476. +        break;
  477.  
  478.      case MAIL_CMD:
  479.          strip_rfc822_comments(data);
  480.          if (out && data[0] == '\0') {
  481.          fprintf(out, "501 MAIL FROM requires address as operand\r\n");
  482. -        fflush(out);
  483.          break;
  484.          }
  485.          if (sender) {
  486.          if (out) {
  487.              fprintf(out, "503 Sender already specified\r\n");
  488. -            fflush(out);
  489.          }
  490.          break;
  491.          }
  492. -        sender = preparse_address(data, &error);
  493. +        sender = preparse_address_1(data, &error, &rest);
  494.          if (out) {
  495. -        if (sender) {
  496. -            fprintf(out, "250 <%s> ... Sender Okay\r\n",
  497. -                   sender);
  498. -        } else {
  499. +        if (!sender) {
  500.              fprintf(out, "501 <%s> ... %s\r\n", data, error);
  501. +            break;
  502.          }
  503. -        fflush(out);
  504.          }
  505.          if (sender && sender[0] == '\0') {
  506.          /* special error sender form <> given */
  507. @@ -258,26 +328,143 @@
  508.          /* special smail-internal <+> was given */
  509.          sender = COPY_STRING("<+>");
  510.          }
  511. +        {
  512. +        char * format = 0;
  513. +        int format_length = 0;
  514. +        BodyFormat body_format = BF_unspecified;
  515. +        BodySize body_size = BS_UNSPECIFIED;
  516. +
  517. +        while (rest && *rest != '\0') {
  518. +            /* maybe we have an extended MAIL command */
  519. +            while (*rest != '\0' && isspace (*rest)) {
  520. +            ++rest;
  521. +            }
  522. +            {
  523. +            int restlen = 0;
  524. +            while (*(rest+restlen) != 0
  525. +                   && !isspace (*(rest+restlen))
  526. +                   && *(rest+restlen) != '=') {
  527. +                ++restlen;
  528. +            }
  529. +            if (strncmpic(rest, "SIZE", restlen) == 0) {
  530. +                rest += restlen;
  531. +                if (*rest != '=') {
  532. +                if (out) {
  533. +                    *(rest+restlen) = '\0';
  534. +                    fprintf(out, "555 missing SIZE parameter\r\n",
  535. +                        rest);
  536. +                }
  537. +                goto fail_mail_cmd;
  538. +                }
  539. +                ++rest;
  540. +                restlen = 0;
  541. +                body_size = 0;
  542. +                while (*(rest+restlen) != 0
  543. +                   && isdigit (*(rest+restlen))
  544. +                   && (!accepted_msg_size || body_size <= accepted_msg_size)) {
  545. +                body_size = 10*body_size+*(rest+restlen)-'0';
  546. +                ++restlen;
  547. +                }
  548. +                if (accepted_msg_size && body_size > accepted_msg_size) {
  549. +                if (out) {
  550. +                    fprintf(out, "552 message too large\r\n");
  551. +                }
  552. +                goto fail_mail_cmd;
  553. +                } else if (*(rest+restlen) != 0
  554. +                       && !isspace (*(rest+restlen))) {
  555. +                if (out) {
  556. +                    while (*(rest+restlen) != 0
  557. +                       && !isspace (*(rest+restlen))) {
  558. +                    ++restlen;
  559. +                    }
  560. +                    *(rest+restlen) = '\0';
  561. +                    fprintf(out, "555 malformed SIZE clause %s\r\n",
  562. +                        rest);
  563. +                }
  564. +                goto fail_mail_cmd;
  565. +                }
  566. +            } else
  567. +#ifdef HAVE_ESMTP_8BITMIME
  568. +            if (strncmpic(rest, "BODY", restlen) == 0) {
  569. +                rest += restlen;
  570. +                if (*rest != '=') {
  571. +                if (out) {
  572. +                    *(rest+restlen) = '\0';
  573. +                    fprintf(out, "555 missing BODY parameter\r\n",
  574. +                        rest);
  575. +                }
  576. +                goto fail_mail_cmd;
  577. +                }
  578. +                ++rest;
  579. +                restlen = 0;
  580. +                while (*(rest+restlen) != 0
  581. +                   && !isspace (*(rest+restlen))) {
  582. +                ++restlen;
  583. +                }
  584. +                if (strncmpic(rest, "7BIT", restlen) == 0) {
  585. +                body_format = BF_7bit;
  586. +                } else if (strncmpic(rest, "8BITMIME", restlen) == 0) {
  587. +                body_format = BF_8bitmime;
  588. +                } else {
  589. +                if (out) {
  590. +                    *(rest+restlen) = '\0';
  591. +                    fprintf(out, "555 unknown BODY type %s\r\n",
  592. +                        rest);
  593. +                }
  594. +                goto fail_mail_cmd;
  595. +                }
  596. +            } else 
  597. +#endif
  598. +            {
  599. +                if (out) {
  600. +                *(rest+restlen) = '\0';
  601. +                fprintf(out, "555 Unknown MAIL TO: option %s\r\n", rest);
  602. +                }
  603. +                goto fail_mail_cmd;
  604. +            }
  605. +            rest += restlen;
  606. +            }
  607. +        }
  608. +        if (out) {
  609. +            if (format && *format != '\0') {
  610. +            *(format+format_length) = '\0';
  611. +            fprintf(out, "250 <%s> ... Sender Okay, using format %s\r\n",
  612. +                sender, format);
  613. +            } else {
  614. +            fprintf(out, "250 <%s> ... Sender Okay\r\n",
  615. +                sender);
  616. +            }
  617. +        }
  618. +        }
  619. +        break;
  620. +      fail_mail_cmd:
  621. +        if (sender) {
  622. +        xfree(sender);
  623. +        sender = NULL;
  624. +        }
  625.          break;
  626.  
  627.      case RCPT_CMD:
  628.          strip_rfc822_comments(data);
  629.          if (out && data[0] == '\0') {
  630.          fprintf(out, "501 RCPT TO requires address as operand\r\n");
  631. -        fflush(out);
  632.          break;
  633.          }
  634.          cur = alloc_addr();
  635.          if (out) {
  636. +#ifdef VERIFY_RCPTS
  637. +        if (! verify_addr (data, out, 1)) {
  638. +            break;
  639. +        }
  640. +#else /* not VERIFY_RCPTS */
  641.          if (cur->work_addr = preparse_address(data, &error)) {
  642.              fprintf(out, "250 <%s> ... Recipient Okay\r\n",
  643.                  cur->work_addr);
  644. -            fflush(out);
  645.          } else {
  646.              fprintf(out, "501 <%s> ... %s\r\n", data, error);
  647. -            fflush(out);
  648.              break;
  649.          }
  650. +#endif
  651.          }
  652.          /*
  653.           * surround in angle brackets, if the addr begins with `-'.
  654. @@ -404,7 +591,7 @@
  655.          fprintf(out, "502 Command not implemented\r\n");
  656.  #else
  657.          strip_rfc822_comments(data);
  658. -        verify_addr(data, out);
  659. +        verify_addr(data, out, 0);
  660.          fflush(out);
  661.  #endif
  662.          }
  663. @@ -438,7 +625,6 @@
  664.          reset_state();
  665.          if (out) {
  666.          fprintf(out, "250 Reset state\r\n");
  667. -        fflush(out);
  668.          }
  669.          break;
  670.  
  671. @@ -549,18 +735,22 @@
  672.  }
  673.  
  674.  static enum e_smtp_commands
  675. -read_smtp_command(f)
  676. +read_smtp_command(f, out)
  677.      register FILE *f;            /* SMTP command stream */
  678. +    register FILE *out;            /* output, may have to be flushed */
  679.  {
  680.      static struct str input;        /* buffer storing recent command */
  681.      static int inited = FALSE;        /* TRUE if input initialized */
  682.      register int c;            /* input char */
  683. +    int flushed_p = !out;
  684. +
  685.      static struct smtp_cmd_list {
  686.      char *name;
  687.      int len;
  688.      enum e_smtp_commands cmd;
  689.      } smtp_cmd_list[] = {
  690.      "HELO",     sizeof("HELO")-1,    HELO_CMD,
  691. +    "EHLO",     sizeof("EHLO")-1,    EHLO_CMD,
  692.      "MAIL FROM:",    sizeof("MAIL FROM:")-1,    MAIL_CMD,
  693.      "RCPT TO:",    sizeof("RCPT TO:")-1,    RCPT_CMD,
  694.      "DATA",        sizeof("DATA")-1,    DATA_CMD,
  695. @@ -580,7 +770,15 @@
  696.      } else {
  697.      input.i = 0;
  698.      }
  699. -    while ((c = getc(f)) != '\n' && c != EOF) {
  700. +    for (;;) {
  701. +    if (!flushed_p && IOB_MAYBE_EMPTY_P (f)) {
  702. +        ++flushed_p;
  703. +        fflush (out);
  704. +    }
  705. +    c = getc(f);
  706. +    if (c == EOF || c == '\n') {
  707. +        break;
  708. +    }
  709.      STR_NEXT(&input, c);
  710.      }
  711.      if (input.p[input.i - 1] == '\r') {
  712. @@ -665,24 +863,26 @@
  713.   *
  714.   * redisplay the input address if it is a valid address.
  715.   */
  716. -static void
  717. -verify_addr(in_addr, out)
  718. +static int
  719. +verify_addr(in_addr, out, rcpt_p)
  720.      char *in_addr;            /* input address string */
  721.      FILE *out;                /* write expansion here */
  722. +    int rcpt_p;                /* non-zero if called from RCPT */
  723.  {
  724.      struct addr *addr = alloc_addr();    /* get an addr structure */
  725.      struct addr *okay = NULL;        /* verified address */
  726.      struct addr *defer = NULL;        /* temporarily unverifiable addr */
  727.      struct addr *fail = NULL;        /* unverified addr */
  728.      char *error;            /* hold error message */
  729. +    int error_code;            /* reply code for negative result */
  730.  
  731. +    error_code = rcpt_p ? 501 : 550;
  732.      addr->in_addr = in_addr;        /* setup the input addr structure */
  733.      /* build the mungeable addr string */
  734.      addr->work_addr = preparse_address(in_addr, &error);
  735.      if (addr->work_addr == NULL) {
  736. -    fprintf(out, "501 %s ... %s\r\n", in_addr, error);
  737. -    fflush(out);
  738. -    return;
  739. +    fprintf(out, "%d %s ... %s\r\n", error_code, in_addr, error);
  740. +    return 0;
  741.      }
  742.  
  743.      /* cache directors and routers on the assumption we will need them again */
  744. @@ -698,15 +898,20 @@
  745.  
  746.      if (okay) {
  747.      fprintf(out, "250 %s\r\n", in_addr);
  748. +    return 1;
  749.      } else if (defer) {
  750. -    fprintf(out, "550 %s ... cannot verify: %s\r\n", in_addr,
  751. -        defer->error->message);
  752. +    fprintf(out, "%d %s ... cannot verify: %s\r\n", 
  753. +        rcpt_p ? 250 : 550, in_addr, defer->error->message);
  754. +    return 1;
  755.      } else if (fail) {
  756. -    fprintf(out, "550 %s ... not matched: %s\r\n", in_addr,
  757. -        fail->error->message);
  758. +    fprintf(out, "%d %s ... not matched: %s\r\n",
  759. +        error_code, in_addr, fail->error->message);
  760. +    return 0;
  761.      } else {
  762.      /* hmmm, it should have been in one of the lists */
  763. -    fprintf(out, "550 %s ... not matched\r\n", in_addr);
  764. +    fprintf(out, "%d %s ... not matched\r\n",
  765. +        error_code, in_addr);
  766. +    return 0;
  767.      }
  768.  }
  769.  #endif    /* NO_VERIFY */
  770. @@ -806,3 +1011,17 @@
  771.      unlink_spool();
  772.      exit(EX_OSFILE);
  773.  }
  774. +
  775. +#ifdef HAVE_DF_SPOOL
  776. +static long
  777. +compute_max_message_size_from_df_spool (void)
  778. +{
  779. +    long free_bytes = spool_max_free_space ();
  780. +    const long reserved = 2*1024*1024;
  781. +    const long min_max_message_size = 20*1024;
  782. +
  783. +    if (free_bytes == -1)
  784. +    return free_bytes;
  785. +    return free_bytes < 2*reserved ? -1 : free_bytes - reserved;
  786. +}
  787. +#endif
  788. --- src/spool.c    1994/11/14 16:53:48    1.1
  789. +++ src/spool.c    1994/11/22 10:59:28
  790. @@ -1623,6 +1623,63 @@
  791.  }
  792.  
  793.  
  794. +#ifdef HAVE_DF_SPOOL
  795. +#ifdef HAVE_STATVFS
  796. +#include <sys/statvfs.h>
  797. +#else /* not HAVE_STATVFS */
  798. +#ifdef HAVE_SYS_STATFS_H
  799. +#include <sys/statfs.h>
  800. +#else /* not HAVE_SYS_STATFS_H */
  801. +#include <sys/vfs.h>
  802. +#endif /* not HAVE_SYS_STATFS_H */
  803. +#endif /* not HAVE_STATVFS */
  804. +
  805. +long
  806. +spool_max_free_space ()
  807. +{
  808. +    char *dirs = spool_dirs;
  809. +    char dirname[SIZE_FILNAM];
  810. +    char *p;
  811. +    long max_free_bytes = -1, free_bytes;
  812. +    int result;
  813. +#ifdef HAVE_STATVFS
  814. +    struct statvfs buf;
  815. +#else
  816. +    struct statfs buf;
  817. +#endif
  818. +
  819. +    while (dirs && *dirs) {
  820. +    for (p = dirname; *dirs && *dirs != ':'; *p++ = *dirs++) ;
  821. +    if (*dirs == ':') {
  822. +        /* next try gets the next directory */
  823. +        dirs++;
  824. +    }
  825. +    *p++ = '\0';            /* terminate directory name */
  826. +#ifdef HAVE_STATVFS
  827. +    result = statvfs (dirname, &buf);
  828. +#else
  829. +    result = statfs (dirname, &buf, sizeof buf, 0);
  830. +#endif
  831. +    if (result == -1)
  832. +        continue;
  833. +#ifdef HAVE_STATVFS
  834. +    free_bytes = buf.f_bfree * buf.f_frsize;
  835. +#else
  836. +    free_bytes = buf.f_bfree * buf.f_bsize;
  837. +#endif
  838. +    if (free_bytes > max_free_bytes)
  839. +        max_free_bytes = free_bytes;
  840. +    }
  841. +    return max_free_bytes;
  842. +}
  843. +#else /* not HAVE_DF_SPOOL */
  844. +long
  845. +spool_max_free_space ()
  846. +{
  847. +    return -1;
  848. +}
  849. +#endif /* not HAVE_DF_SPOOL */
  850. +
  851.  #ifdef STANDALONE
  852.  
  853.  #include "varargs.h"
  854. --- src/transports/smtplib.c    1994/11/14 09:32:24    1.1
  855. +++ src/transports/smtplib.c    1994/11/14 21:51:10
  856. @@ -30,6 +30,9 @@
  857.  # include "../error.h"
  858.  # include "../debug.h"
  859.  #endif
  860. +#if !defined(NO_LOG_EHLO)
  861. +#  include "../log.h"
  862. +#endif
  863.  
  864.  #ifdef ANSI_C
  865.  # define P_(x)  x
  866. @@ -40,6 +43,7 @@
  867.  #endif
  868.  
  869.  /* supported SMTP commands */
  870. +#define EHLO(domain)    "EHLO %s", domain
  871.  #define HELO(domain)    "HELO %s", domain
  872.  #define MAIL_BEGIN    "MAIL FROM:<"
  873.  #define MAIL_END    ">"
  874. @@ -69,6 +73,8 @@
  875.  /* pseudo-reply codes */
  876.  #define REPLY_PROTO_ERROR    0x498    /* protocol error on read */
  877.  #define REPLY_TIMEOUT        0x499    /* timeout on read, or EOF on read */
  878. +#define REPLY_NOT_ACCEPTABLE    0x501    /* request not acceptable */
  879. +#define REPLY_SEQUENCE_ERROR    0x503    /* Bad sequence of commands */
  880.  
  881.  /* variables local to this file */
  882.  static struct str smtp_out;        /* region for outgoing commands */
  883. @@ -77,18 +83,45 @@
  884.  static JUMP_ENVBUF timeout_buf;        /* timeouts jump here */
  885.  
  886.  /* functions local to this file */
  887. +#ifdef __STDC__
  888. +extern int smtp_startup(struct smtp *, struct error **);
  889. +extern int smtp_send(struct smtp *, struct addr *, struct addr **, struct addr **, struct addr **, struct error **);
  890. +extern void smtp_shutdown(struct smtp *);
  891. +static void do_smtp_shutdown(struct smtp *, int);
  892. +static int write_command_maybe_wait(struct smtp *, unsigned, char *, int, char **);
  893. +static int wait_write_command(struct smtp *, unsigned, char *, int, char **);
  894. +static int write_command_nowait(struct smtp *, char *, int);
  895. +static int flush_command_stream(struct smtp *, char **);
  896. +static int wait_read_response(struct smtp *, unsigned, char **);
  897. +static int read_response_internal(struct smtp *, unsigned, char **);
  898. +static void catch_timeout(void);
  899. +static struct error * no_remote(struct transport *, char *);
  900. +static struct error * try_again(struct transport *, char *);
  901. +static struct error * fatal_error(struct transport *, char *);
  902. +static struct error * remote_full(struct transport *, char *);
  903. +static struct error * remote_bad_address(struct transport *, char *);
  904. +static struct error * write_failed(struct transport *);
  905. +static struct error * read_failed(struct transport *); 
  906. +#else /* not __STDC__ */
  907. +extern int smtp_startup();
  908. +extern int smtp_send();
  909. +extern void smtp_shutdown();
  910.  static void do_smtp_shutdown();
  911. +static int write_command_maybe_wait();
  912.  static int wait_write_command();
  913. +static int write_command_nowait();
  914. +static int flush_command_stream();
  915.  static int wait_read_response();
  916. +static int read_response_internal();
  917.  static void catch_timeout();
  918. -static struct error *no_remote();
  919. -static struct error *try_again();
  920. -static struct error *fatal_error();
  921. -static struct error *remote_full();
  922. -static struct error *remote_bad_address();
  923. -static struct error *write_failed();
  924. -static struct error *read_failed();
  925. -
  926. +static struct error * no_remote();
  927. +static struct error * try_again();
  928. +static struct error * fatal_error();
  929. +static struct error * remote_full();
  930. +static struct error * remote_bad_address();
  931. +static struct error * write_failed();
  932. +static struct error * read_failed(); 
  933. +#endif /* not __STDC__ */
  934.  
  935.  /*
  936.   * smtp_startup - initiate contact on an SMTP connection
  937. @@ -111,6 +144,9 @@
  938.  {
  939.      int reply;
  940.      char *reply_text;
  941. +#ifdef HAVE_EHLO
  942. +    int tried_rset = 0;
  943. +#endif
  944.  
  945.      if (! smtp_init_flag) {
  946.      STR_INIT(&smtp_in);
  947. @@ -134,6 +170,102 @@
  948.      return SMTP_AGAIN;
  949.      }
  950.  
  951. +#ifdef HAVE_EHLO
  952. +    /*
  953. +     * say who we are.
  954. +     * Possible responses:
  955. +     *    250 - okay        (continue conversation)
  956. +     *    421 - closing down  (try again later)
  957. +     *  5xx - fatal error   (try HELO)
  958. +     */
  959. +    smtp_out.i = 0;
  960. +    (void) str_printf(&smtp_out, EHLO(primary_name));
  961. +
  962. +    reply = wait_write_command(smtpb, smtpb->short_timeout,
  963. +                   smtp_out.p, smtp_out.i, &reply_text);
  964. +
  965. +    if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  966. +    /* remote SMTP closed, try again later */
  967. +    *error_p = try_again(smtpb->tp, reply_text);
  968. +    return SMTP_AGAIN;
  969. +    }
  970. +    if (reply == REPLY_OK) {
  971. +    char * cp = reply_text;
  972. +    int on_greet_line = 1;
  973. +
  974. +    smtpb->smtp_flags = ESMTP_basic;
  975. +    /* Parse the EHLO reply to find out
  976. +       what the remote server supports */
  977. +    while (*cp != 0) {
  978. +        int skip;
  979. +        int keywordlength;
  980. +
  981. +        for (skip = 4; *cp != 0 && skip; --skip, ++cp) {
  982. +        if (skip == 1
  983. +            ? (*cp != ' ' && *cp != '-')
  984. +            : (! isdigit (*cp))) {
  985. +            goto malformed_ehlo_reply;
  986. +        }
  987. +        }
  988. +        if (skip != 0) {
  989. +        goto malformed_ehlo_reply;
  990. +        }
  991. +        if (on_greet_line) {
  992. +        /*
  993. +         * Ignore greeting on first line
  994. +         */
  995. +        on_greet_line = 0;
  996. +        goto skip_rest_of_line;
  997. +        }
  998. +        for (keywordlength = 0;
  999. +         *(cp+keywordlength) != 0
  1000. +         && !isspace (*(cp+keywordlength));
  1001. +         ++keywordlength)
  1002. +        ;
  1003. +        if (strncmpic(cp, "SIZE", keywordlength) == 0) {
  1004. +        unsigned long max_size = 0;
  1005. +
  1006. +        cp += keywordlength;
  1007. +        while (*cp == ' ' || *cp == '\t')
  1008. +        ++cp;
  1009. +        if (!isdigit(*cp) && *cp != '\n')
  1010. +        goto malformed_ehlo_reply;
  1011. +        while (isdigit(*cp)) {
  1012. +            max_size *= 10;
  1013. +            max_size += *cp - '0';
  1014. +            ++cp;
  1015. +        }
  1016. +        smtpb->smtp_flags |= ESMTP_size;
  1017. +        smtpb->max_size = max_size;
  1018. +        } else if (strncmpic(cp, "8BITMIME", keywordlength) == 0) {
  1019. +        smtpb->smtp_flags |= ESMTP_8bitmime;
  1020. +        } else if (strncmpic(cp, "PIPELINING", keywordlength) == 0) {
  1021. +        smtpb->smtp_flags |= ESMTP_pipelining;
  1022. +        } else {
  1023. +        }
  1024. +      skip_rest_of_line:
  1025. +        while (*cp != 0 && *cp != '\n')
  1026. +        ++cp;
  1027. +        if (*cp == '\n')
  1028. +        ++cp;
  1029. +    }
  1030. +#ifndef NO_LOG_EHLO
  1031. +    write_log(LOG_SYS, "destination supports esmtp%s%s%s",
  1032. +          smtpb->smtp_flags & ESMTP_8bitmime ? " 8BITMIME" : "",
  1033. +          smtpb->smtp_flags & ESMTP_size ? " SIZE" : "",
  1034. +          smtpb->smtp_flags & ESMTP_pipelining ? " PIPELINING" : "");
  1035. +#endif /* not NO_LOG_EHLO */
  1036. +    return SMTP_SUCCEED;
  1037. +      malformed_ehlo_reply:
  1038. +    /* This seems to be a reasonable way
  1039. +       to handle malformed EHLO replies: */
  1040. +#ifndef NO_LOG_EHLO
  1041. +    write_log(LOG_SYS, "destination supports esmtp, but is buggy (%s)",
  1042. +          reply_text);
  1043. +#endif /* not NO_LOG_EHLO */
  1044. +    return SMTP_SUCCEED;
  1045. +    }
  1046. +#endif
  1047.      /*
  1048.       * say who we are.
  1049.       * Possible responses:
  1050. @@ -141,6 +273,9 @@
  1051.       *    421 - closing down  (try again later)
  1052.       *  5xx - fatal error   (return message to sender)
  1053.       */
  1054. +#ifdef HAVE_EHLO
  1055. +  try_helo:
  1056. +#endif
  1057.      smtp_out.i = 0;
  1058.      (void) str_printf(&smtp_out, HELO(primary_name));
  1059.  
  1060. @@ -152,10 +287,47 @@
  1061.      *error_p = try_again(smtpb->tp, reply_text);
  1062.      return SMTP_AGAIN;
  1063.      }
  1064. -    if (reply != REPLY_OK) {
  1065. +    if (reply == REPLY_SEQUENCE_ERROR) {
  1066. +    /* ignore 503 Bad sequence of commands after EHLO/RSET/HELO */
  1067. +#ifndef NO_LOG_EHLO
  1068. +    write_log(LOG_SYS, "503 after EHLO/RSET/HELO (%s)",
  1069. +          reply_text);
  1070. +#endif /* not NO_LOG_EHLO */
  1071. +    } else if (reply != REPLY_OK) {
  1072. +#ifdef HAVE_EHLO
  1073. +    if (! tried_rset) {
  1074. +        /* The following */
  1075. +        /* fatal error, probably doesn't understand EHLO */
  1076. +        smtp_out.i = 0;
  1077. +        (void) str_printf(&smtp_out, "RSET");
  1078. +
  1079. +        reply = wait_write_command(smtpb, smtpb->short_timeout,
  1080. +                       smtp_out.p, smtp_out.i, &reply_text);
  1081. +        if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1082. +        /* remote SMTP closed, try again later */
  1083. +        *error_p = try_again(smtpb->tp, reply_text);
  1084. +        return SMTP_AGAIN;
  1085. +        } else if (reply == REPLY_OK) {
  1086. +        } else {
  1087. +        /* Some gateways don't accept any commands before they get
  1088. +           a HELO, not even RSET.  Fortunately it is usually safe
  1089. +           to ignore the error messages. */
  1090. +#ifndef NO_LOG_EHLO
  1091. +        write_log(LOG_SYS, "unexpected response to RSET (%s)",
  1092. +              reply_text);
  1093. +#endif /* not NO_LOG_EHLO */
  1094. +        }
  1095. +        /* the RSET command has been accepted; try again with HELO */
  1096. +        tried_rset = 1;
  1097. +        goto try_helo;    /* GASP!!! */
  1098. +    } else {
  1099. +#endif /* HAVE_EHLO */
  1100.      /* fatal error, return message to sender */
  1101.      *error_p = fatal_error(smtpb->tp, reply_text);
  1102.      return SMTP_FAIL;
  1103. +#ifdef HAVE_EHLO
  1104. +    }
  1105. +#endif
  1106.      }
  1107.  
  1108.      /* connection established */
  1109. @@ -221,11 +393,36 @@
  1110.          get_sender_addr(tp)),
  1111.             MAIL_END);
  1112.  
  1113. -    reply = wait_write_command(smtpb, smtpb->long_timeout,
  1114. +    /*
  1115. +     * send (a guess of) the size of the message to be transported.
  1116. +     * Of course the guess could be a bit more educated, but usually
  1117. +     * it doesn't matter if it is slightly incorrect.  We simply add
  1118. +     * 2% in order to account for \n -> \r\n conversion.
  1119. +     */
  1120. +    if (smtpb->smtp_flags & ESMTP_size) {
  1121. +    str_printf(&smtp_out, " SIZE=%lu",
  1122. +           (unsigned long) (msg_size * 1.02));
  1123. +    }
  1124. +#if 0
  1125. +    /*
  1126. +     * This is commented out because we are not supposed to send a
  1127. +     * non-MIME message on 8BITMIME mode.  But sending a non-7bit
  1128. +     * clean message in 7BIT mode isn't a good idea either.  Sigh.
  1129. +     * Something more sophisticated is definitely needed here.
  1130. +     */
  1131. +    if (smtpb->smtp_flags & ESMTP_8bitmime) {
  1132. +    str_printf(&smtp_out, " BODY 8BITMIME");
  1133. +    }
  1134. +#endif
  1135. +
  1136. +    /* give all of the recipient addresses to the remote SMTP */
  1137. +    okay = NULL;
  1138. +
  1139. +    reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
  1140.                     smtp_out.p, smtp_out.i, &reply_text);
  1141.  
  1142.      if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1143. -    *error_p = try_again(smtpb->tp, reply_text);
  1144. +    *error_p = try_again(tp, reply_text);
  1145.      insert_addr_list(addr, defer, *error_p);
  1146.      do_smtp_shutdown(smtpb, reply);
  1147.      return FAIL;
  1148. @@ -253,15 +450,15 @@
  1149.  
  1150.      str_printf(&smtp_out, "%s%s%s", RCPT_BEGIN, cur->next_addr, RCPT_END);
  1151.  
  1152. -    reply = wait_write_command(smtpb, smtpb->long_timeout,
  1153. +    reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
  1154.                     smtp_out.p, smtp_out.i, &reply_text);
  1155.  
  1156. -    if (reply == REPLY_STORAGE_FULL
  1157. -        || REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN)
  1158. -    {
  1159. -        *error_p = (reply == REPLY_STORAGE_FULL)?
  1160. -        remote_full(tp, reply_text):
  1161. -        try_again(smtpb->tp, reply_text);
  1162. +    if (reply == REPLY_STORAGE_FULL) {
  1163. +        *error_p = remote_full(tp, reply_text);
  1164. +        insert_addr_list(cur, defer, *error_p);
  1165. +        next = NULL;
  1166. +    } else if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1167. +        *error_p = try_again(tp, reply_text);
  1168.          insert_addr_list(cur, defer, *error_p);
  1169.          insert_addr_list(okay, defer, *error_p);
  1170.          do_smtp_shutdown(smtpb, reply);
  1171. @@ -285,9 +482,70 @@
  1172.       *    4xx - remote error       (try all recipients again later)
  1173.       *  5xx - fatal error        (return message to sender)
  1174.       */
  1175. -    reply = wait_write_command(smtpb, smtpb->long_timeout,
  1176. +    reply = write_command_maybe_wait(smtpb, smtpb->long_timeout,
  1177.                     DATA, sizeof(DATA) - 1, &reply_text);
  1178.  
  1179. +    if (smtpb->smtp_flags & ESMTP_pipelining) {
  1180. +
  1181. +    if (reply != REPLY_OK) {
  1182. +        *error_p = try_again(tp, reply_text);
  1183. +        insert_addr_list(addr, defer, *error_p);
  1184. +        do_smtp_shutdown(smtpb, reply);
  1185. +        return FAIL;
  1186. +    }
  1187. +
  1188. +    if (flush_command_stream(smtpb, &reply_text) != REPLY_OK) {
  1189. +        return FAIL;
  1190. +    }
  1191. +
  1192. +    /* Now read all the responses.  First the MAIL FROM: reply */
  1193. +    reply = wait_read_response(smtpb, smtpb->long_timeout,
  1194. +                   &reply_text);
  1195. +    if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1196. +        *error_p = try_again(tp, reply_text);
  1197. +        insert_addr_list(addr, defer, *error_p);
  1198. +        do_smtp_shutdown(smtpb, reply);
  1199. +        return FAIL;
  1200. +    } else if (REPLY_GROUP(reply) == NEGATIVE_FAILED) {
  1201. +        *error_p = fatal_error(tp, reply_text);
  1202. +        insert_addr_list(addr, fail, *error_p);
  1203. +        do_smtp_shutdown(smtpb, reply);
  1204. +        return FAIL;
  1205. +    }
  1206. +
  1207. +    addr = okay;
  1208. +    okay = NULL;
  1209. +
  1210. +    for (cur = addr; cur; cur = next) {
  1211. +        next = cur->succ;
  1212. +
  1213. +        reply = wait_read_response(smtpb, smtpb->long_timeout,
  1214. +                       &reply_text);
  1215. +
  1216. +        if (reply == REPLY_STORAGE_FULL) {
  1217. +        *error_p = remote_full(tp, reply_text);
  1218. +        insert_addr_list(cur, defer, *error_p);
  1219. +        next = NULL;
  1220. +        } else if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1221. +        *error_p = try_again(tp, reply_text);
  1222. +        insert_addr_list(cur, defer, *error_p);
  1223. +        insert_addr_list(okay, defer, *error_p);
  1224. +        do_smtp_shutdown(smtpb, reply);
  1225. +        return FAIL;
  1226. +        } else if (REPLY_GROUP(reply) == NEGATIVE_FAILED) {
  1227. +        cur->error =  remote_bad_address(tp, reply_text);
  1228. +        cur->succ = *fail;
  1229. +        *fail = cur;
  1230. +        } else {
  1231. +        /* successful thus far */
  1232. +        cur->succ = okay;
  1233. +        okay = cur;
  1234. +        }
  1235. +    }
  1236. +    reply = wait_read_response(smtpb, smtpb->long_timeout,
  1237. +                   &reply_text);
  1238. +    }
  1239. +
  1240.      if (REPLY_GROUP(reply) == NEGATIVE_TRY_AGAIN) {
  1241.      *error_p = try_again(tp, reply_text);
  1242.      insert_addr_list(okay, defer, *error_p);
  1243. @@ -306,7 +564,7 @@
  1244.      /*
  1245.       * send the message, using the hidden dot protocol.
  1246.       */
  1247. -    smtpb->tp->flags |= PUT_DOTS;
  1248. +    tp->flags |= PUT_DOTS;
  1249.      success = write_message(smtpb->out, tp, addr);
  1250.  
  1251.      if (success == WRITE_FAIL) {
  1252. @@ -381,6 +639,21 @@
  1253.      }
  1254.  }
  1255.  
  1256. +static int
  1257. +write_command_maybe_wait (smtpb, timeout, text, len, reply_text)
  1258. +    struct smtp *smtpb;            /* SMTP description block */
  1259. +    unsigned timeout;            /* read timeout */
  1260. +    char *text;                /* text of command */
  1261. +    register int len;            /* length of command */
  1262. +    char **reply_text;            /* text of response from remote */
  1263. +{
  1264. +    if (smtpb->smtp_flags & ESMTP_pipelining) {
  1265. +    return write_command_nowait (smtpb, text, len);
  1266. +    } else {
  1267. +    return wait_write_command (smtpb, timeout, text, len, reply_text);
  1268. +    }
  1269. +}
  1270. +
  1271.  /*
  1272.   * wait_write_command - send a command, then wait for the response
  1273.   *
  1274. @@ -395,6 +668,30 @@
  1275.      register int len;            /* length of command */
  1276.      char **reply_text;            /* text of response from remote */
  1277.  {
  1278. +    int reply;
  1279. +
  1280. +    reply = write_command_nowait (smtpb, text, len);
  1281. +    if (reply != REPLY_OK)
  1282. +    return reply;
  1283. +
  1284. +    reply = flush_command_stream (smtpb, reply_text);
  1285. +    if (reply != REPLY_OK)
  1286. +    return reply;
  1287. +
  1288. +    /* wait for the response to come back */
  1289. +    reply = wait_read_response(smtpb, timeout, reply_text);
  1290. +    return reply;
  1291. +}
  1292. +
  1293. +/*
  1294. + * write_command_nowait - send a command
  1295. + */
  1296. +static int
  1297. +write_command_nowait(smtpb, text, len)
  1298. +    struct smtp *smtpb;            /* SMTP description block */
  1299. +    char *text;                /* text of command */
  1300. +    register int len;            /* length of command */
  1301. +{
  1302.      register FILE *f = smtpb->out;
  1303.      register char *cp;
  1304.      int reply;
  1305. @@ -421,19 +718,22 @@
  1306.      for (cp = smtpb->nl; *cp; cp++) {
  1307.      putc(*cp, f);
  1308.      }
  1309. +    return REPLY_OK;
  1310. +}
  1311. +
  1312. +static int
  1313. +flush_command_stream(smtpb, reply_text)
  1314. +    struct smtp *smtpb;            /* SMTP description block */
  1315. +    char **reply_text;            /* text of response from remote */
  1316. +{
  1317. +    register FILE *f = smtpb->out;
  1318. +
  1319.      (void) fflush(f);
  1320.      if (ferror(f)) {
  1321.      *reply_text = "499 write error, remote probably down";
  1322.      DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
  1323.      return REPLY_TIMEOUT;
  1324.      }
  1325. -
  1326. -    /* wait for the response to come back */
  1327. -    if (smtpb->in) {
  1328. -    reply = wait_read_response(smtpb, timeout, reply_text);
  1329. -    DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
  1330. -    return reply;
  1331. -    }
  1332.      return REPLY_OK;
  1333.  }
  1334.  
  1335. @@ -445,6 +745,23 @@
  1336.   */
  1337.  static int
  1338.  wait_read_response(smtpb, timeout, reply_text)
  1339. +    struct smtp *smtpb;            /* SMTP description block */
  1340. +    unsigned timeout;            /* read timeout */
  1341. +    char **reply_text;            /* return text of response here */
  1342. +{
  1343. +    int result;
  1344. +
  1345. +    /* If we're in batch mode, always return success. */
  1346. +    if (! smtpb->in) {
  1347. +    return REPLY_OK;
  1348. +    }
  1349. +    result = read_response_internal (smtpb, timeout, reply_text);
  1350. +    DEBUG1(DBG_DRIVER_MID, "SMTP-reply: %s\n", *reply_text);
  1351. +    return result;
  1352. +}
  1353. +
  1354. +static int
  1355. +read_response_internal(smtpb, timeout, reply_text)
  1356.      struct smtp *smtpb;            /* SMTP description block */
  1357.      unsigned timeout;            /* read timeout */
  1358.      char **reply_text;            /* return text of response here */
  1359. --- src/transports/smtplib.h    1994/11/14 09:46:49    1.1
  1360. +++ src/transports/smtplib.h    1994/11/14 17:22:11
  1361. @@ -18,6 +18,16 @@
  1362.  #define SMTP_FAIL    (-1)
  1363.  #define SMTP_AGAIN    (-2)
  1364.  
  1365. +typedef enum
  1366. +{
  1367. +    ESMTP_none        = 0x0000,
  1368. +    ESMTP_basic        = 0x0001,
  1369. +    ESMTP_8bitmime    = 0x0002,
  1370. +    ESMTP_size        = 0x0004,
  1371. +    ESMTP_pipelining    = 0x0008
  1372. +}
  1373. +SMTPExtension, SMTPExtensionFlags;
  1374. +
  1375.  /*
  1376.   * the following structure is passed around between SMTP functions and
  1377.   * should be initialized as necessary to describe the SMTP connection
  1378. @@ -32,6 +42,8 @@
  1379.      unsigned long_timeout;        /* normal SMTP read timeout period */
  1380.      char *nl;                /* line terminator string */
  1381.      struct transport *tp;        /* associated transport */
  1382. +    SMTPExtensionFlags smtp_flags;    /* SMTP extensions supported by remote */
  1383. +    unsigned long max_size;        /* message size limit of remote */
  1384.  };
  1385.  
  1386.  /* functions defined in smtplib.c */
  1387. --- src/transports/tcpsmtp.c    1994/11/14 09:47:25    1.1
  1388. +++ src/transports/tcpsmtp.c    1994/11/14 17:21:54
  1389. @@ -330,6 +330,8 @@
  1390.      smtpbuf.nl = "\r\n";
  1391.      tp->flags |= PUT_CRLF;
  1392.      smtpbuf.tp = tp;
  1393. +    smtpbuf.smtp_flags = ESMTP_none;
  1394. +    smtpbuf.max_size = 0;
  1395.  
  1396.      DEBUG(DBG_DRIVER_LO, "connected\n");
  1397.  
  1398. --- /dev/null    Tue Nov 22 12:38:13 1994
  1399. +++ src/geniobpeek.sh    Mon Nov 14 10:48:56 1994
  1400. @@ -0,0 +1,81 @@
  1401. +#!/bin/sh
  1402. +#
  1403. +# This script tries to generate a file "iobpeek.h" which defines one
  1404. +# macro, IOB_MAYBE_EMPTY_P.  The macro can be called with one
  1405. +# argument, a stdio input stream, and must return a non-zero value if
  1406. +# the stream's buffer is empty.  If there are characters in the input
  1407. +# buffer, it *should* return zero.
  1408. +
  1409. +# work around brain-damaged Ultrix shell
  1410. +if test -z "$foo" -a -r /bin/sh5
  1411. +then
  1412. +  export foo; foo=bar; exec /bin/sh5 $0 "$1" "$2"
  1413. +fi
  1414. +
  1415. +CC=$1; shift
  1416. +CFLAGS=$1; shift
  1417. +ECHO=echo
  1418. +#ECHO=:
  1419. +
  1420. +rm -f ,testfile
  1421. +
  1422. +try ()
  1423. +{
  1424. +  echo foo bar > ,testfile
  1425. +  empty_test="$1"
  1426. +  $ECHO "Trying $empty_test"
  1427. +  rm -f ,iobtest.c
  1428. +  cat > ,iobtest.c <<EOM
  1429. +#include <stdio.h>
  1430. +
  1431. +#define IOB_MAYBE_EMPTY_P(stream) $empty_test
  1432. +
  1433. +main (argc, argv) int argc; char **argv;
  1434. +{
  1435. +  FILE * in = fopen(",testfile", "r");
  1436. +  int c;
  1437. +  if (! in) { fprintf (stderr, "Couldn't open test file\n"); exit (1); }
  1438. +  if (! IOB_MAYBE_EMPTY_P (in)) {
  1439. +    fprintf (stderr, "Buffer not empty after fopen()\n"); exit (1);
  1440. +  }
  1441. +  c = getc(in);
  1442. +  if (c != 'f') { fprintf (stderr, "Read error\n"); exit (1); }
  1443. +  if (IOB_MAYBE_EMPTY_P (in)) {
  1444. +    fprintf (stderr, "Buffer empty after reading one char\n"); exit (1);
  1445. +  }
  1446. +  return 0;
  1447. +}
  1448. +EOM
  1449. +
  1450. +  $CC $CFLAGS -o ,iobtest ,iobtest.c >/dev/null 2>&1 || \
  1451. +    ( $ECHO "  compilation failed"; return 1 )
  1452. +  if ./,iobtest
  1453. +  then
  1454. +    cat > iobpeek.h <<EOM
  1455. +/* iobpeek.h - DO NOT EDIT!
  1456. +   Automatically generated by geniobpeek.sh */
  1457. +#define IOB_MAYBE_EMPTY_P(stream) $empty_test
  1458. +EOM
  1459. +    status=0
  1460. +  else
  1461. +    status=1
  1462. +  fi
  1463. +  rm ,iobtest ,iobtest.c ,testfile
  1464. +  return $status
  1465. +}
  1466. +
  1467. +# IRIX, SunOS 4, Solaris 2
  1468. +try '((stream)->_cnt == 0)' && exit 0
  1469. +# Older systems may have this
  1470. +try '((stream)->cnt == 0)' && exit 0
  1471. +# Linux
  1472. +try '((stream)->_IO_read_ptr >= (stream)->_IO_read_end)' && exit 0
  1473. +# GNU libc 1.08.1
  1474. +try '((stream)->bufp >= (stream)->__get_limit)' && exit 0
  1475. +
  1476. +cat 1>&2 <<EOM
  1477. +*** Error: could not find out how to check for an empty stdio stream
  1478. +***        buffer on this system.  Please notify Simon Leinen
  1479. +***        <simon@lia.di.epfl.ch> of this problem.
  1480. +EOM
  1481. +exit 1
  1482.